
#include "World.h"
#include "Player.h"
#include "HumanPlayer.h"
#include "AI/AIPlayer.h"
#include "Ship.h"
#include "Input/Input.h"
#include "Map.h"
#include "GameState.h"
#include "Explosion.h"
#include "Shockwave.h"
#include "Settings.h"

#include <iostream>
#include <cassert>
#include <map>
#include <vector>

static World* world = NULL;

World& World::instance(void){
    if (world == NULL)
        world = new World();
    return *world;
}

void World::reset(void) {
    delete world;
    world = NULL;
}

World::World(){
    
}

World::~World(){

}


void World::load(void){
    my_map = new Map();
    my_map->load(Settings::instance().getStringValue("map","file"));
    createPlayer();
}


void World::update(void){
    
    for(unsigned i = 0; i < all_players.size(); i++){
        assert(all_players[i] != NULL);
        all_players[i]->update();
    }

    std::map<unsigned, WorldObject*>::iterator it = world_objects.begin();
    std::vector<unsigned> to_delete_ids;
    while(it != world_objects.end()){
        assert(it->second != NULL);

        it->second->update();
        it->second->resolveCollision(my_map);

        if(!(it->second->active())){
            to_delete_ids.push_back(it->first);
        }

        ++it;
    }

    for(unsigned i = 0; i < to_delete_ids.size(); i++){
        unsigned id = to_delete_ids[i];
        delete world_objects[id];
        world_objects.erase(world_objects.find(id));
    }
}


void World::render(void){
    my_map->render();

    std::map<unsigned, WorldObject*>::iterator it = world_objects.begin();
    while(it != world_objects.end()){
        assert(it->second != NULL);
        it->second->render();
        ++it;
    }
}


Vector2D World::getCurrentFocusPoint(void) const {
    assert(my_player != NULL);
    return my_player->getFocusPoint();
}


Player* World::getControlPlayer(void) const {
    assert(my_player != NULL);
    return my_player;
}


WorldObjectPointer World::addObject(WorldObject *obj){
    assert(world_objects.find(obj->getId()) == world_objects.end());
    world_objects[obj->getId()] = obj;

    return WorldObjectPointer(obj);
}


WorldObject* World::getObject(unsigned id) const {
    std::map<unsigned, WorldObject*>::const_iterator it = world_objects.find(id);
    if(it == world_objects.end()){ return NULL; }
    if(!it->second->active()){ return NULL; }
    return it->second;
}


std::vector<WorldObject*> World::getAllObjectsOfType(WorldObjectType type) const {
    std::vector<WorldObject*> result;
    std::map<unsigned, WorldObject*>::const_iterator it = world_objects.begin();

    while(it != world_objects.end()){
        if(it->second->getObjectType() == type && it->second->active()){
            result.push_back(it->second);
        }
        ++it;
    }

    return result;
}


std::vector<WorldObject*> World::getObjectsInVicinity(Vector2D pos) const {
    std::vector<WorldObject*> result;    

    std::map<unsigned, WorldObject*>::const_iterator it = world_objects.begin();
    while(it != world_objects.end()){
        assert(it->second != NULL);
        result.push_back(it->second);
        ++it;
    }

    return result;
}


Map* World::getMap(void) const {
    assert(my_map != NULL);
    return my_map;
}


void World::telefrag(Vector2D position, float radius){
    std::map<unsigned, WorldObject*>::iterator it = world_objects.begin();
    std::vector<unsigned> to_delete_ids;

    while(it != world_objects.end()){
        assert(it->second != NULL);
        if(it->second->getObjectType() == SHIP_WOBJECT){
            Ship *ship = (Ship*)(it->second);
            if((ship->getPosition()-position).length() < radius + ship->getSize()){
                to_delete_ids.push_back(it->second->getId());
                ship->die();
            }
        }
        ++it;
    }

    for(unsigned i = 0; i < to_delete_ids.size(); i++){
        unsigned id = to_delete_ids[i];
        delete world_objects[id];
        world_objects.erase(world_objects.find(id));
    }

    // Create a spawn effect.
    Shockwave *s = new Shockwave(position, 1.0f, 0.75f, 1.0f, 0.6f, 0.6f, 1.0f);
    addObject(s);
}


void World::createPlayer(void){
    Player *player = new HumanPlayer(0, "void", 1);
    my_player = player;
    all_players.push_back(player);
    GameState::instance().addPlayerToTeam(player, 1);

/*
    player = new AIPlayer(1, "Foo", 1);
    all_players.push_back(player);
    GameState::instance().addPlayerToTeam(player, 1);
    player = new AIPlayer(2, "Bar", 1);
    all_players.push_back(player);
    GameState::instance().addPlayerToTeam(player, 1);
*/
}

